﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpModelViewUtil.h"
#include "Box3d.h"
#include "TiltBox3d.h"
#include "ModelViewParameterValidator.h"
#include "IDocument.h"
#include "ILevel.h"
#include "IModelView.h"
#include "IViewClipRange.h"
#include "IExternalDataComponent.h"
#include "IPlane.h"
#include "IElementParameters.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "IParameterDefinitionLibrary.h"
#include "StringUtil.h"
#include "GbmpElevationViewInput.h"
#include "IGbmpElevationView.h"
#include "LevelUtils.h"
#include "GbmpModelViewExternalDataId.h"
#include "IExternalData.h"
#include "IGraphicsElementShape.h"
#include "IWorkPlane.h"
#include "IElementBasicInformation.h"
#include "GraphicsNodeUtils.h"
#include "IReferencePlane.h"
#include "Vector3dUtils.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

IModelView* GbmpModelViewUtil::CreateModelView(IDocument* pDoc, const std::wstring& viewName, const BuiltInViewType& viewType,
    const Vector3d& viewDirection,
    const Vector3d& origin,
    const Vector3d& rightDirection,
    const Vector3d upDirection,
    ElementCreationOptions creationOption)
{
    IModelView* pModelView = IModelView::Create(pDoc, viewName, viewType, viewDirection, origin, rightDirection, upDirection, creationOption);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"创建ModelView失败",L"GDMPLab",L"2024-03-30");

    // 定制视图名称的检查行为
    IElementParameters* pParameterBehavior = pModelView->GetElementParameters();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pParameterBehavior, L"pParameterBehavior为空",L"GDMPLab",L"2024-03-30");
    pParameterBehavior->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(ModelViewParameterValidatorOverride, PARAMETER_UID(ElementNameBuiltInParameter)));

    return pModelView;
}

bool GbmpModelViewUtil::IsPlanOrCeilingView(const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    //暂时先禁止掉图纸视图Ctrl + C
    if (pModelView->GetViewType() == BuiltInViewType::Drawing)
        return false;
    //暂时先禁止掉图纸视图Ctrl + C

    const IGbmpModelView* pGbmpModelView = GbmpModelViewUtil::GetGbmpModelView(pModelView);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pGbmpModelView, L"pGbmpModelView为空",L"GDMPLab",L"2024-03-30");
    return (PlanViewType == pGbmpModelView->GetViewType()) || (CeilingViewType == pGbmpModelView->GetViewType());
}

IGbmpElevationView* GbmpModelViewUtil::CreateEastElevationView(IDocument* pDoc)
{
    GbmpElevationViewInput input;
    input.Document = pDoc;
    input.Name = GBMP_TR(L"东");
    input.ViewDirection = Vector3d::NegaUnitX;
    input.Origin = Vector3d(25000.0f, 0, 0);
    input.RightDirection = Vector3d::UnitY;
    input.UpDirection = Vector3d::UnitZ;
    input.HasRectViewport = true;
    input.RectViewportWidth = 25000.0f;
    input.RectViewportHeight = 30000.0f;
    input.IsBuiltIn = true;

    return IGbmpElevationView::Create(input);
}

IGbmpElevationView* GbmpModelViewUtil::CreateWestElevationView(IDocument* pDoc)
{
    GbmpElevationViewInput input;
    input.Document = pDoc;
    input.Name = GBMP_TR(L"西");
    input.ViewDirection = Vector3d::UnitX;
    input.Origin = Vector3d(-25000.0f, 0, 0);
    input.RightDirection = Vector3d::NegaUnitY;
    input.UpDirection = Vector3d::UnitZ;
    input.HasRectViewport = true;
    input.RectViewportWidth = 25000.0f;
    input.RectViewportHeight = 30000.0f;
    input.IsBuiltIn = true;

    return IGbmpElevationView::Create(input);
}

IGbmpElevationView* GbmpModelViewUtil::CreateSouthElevationView(IDocument* pDoc)
{
    GbmpElevationViewInput input;
    input.Document = pDoc;
    input.Name = GBMP_TR(L"南");
    input.ViewDirection = Vector3d::UnitY;
    input.Origin = Vector3d(0, -12500.0f, 0);
    input.RightDirection = Vector3d::UnitX;
    input.UpDirection = Vector3d::UnitZ;
    input.HasRectViewport = true;
    input.RectViewportWidth = 50000.0f;
    input.RectViewportHeight = 30000.0f;
    input.IsBuiltIn = true;

    return IGbmpElevationView::Create(input);
}

IGbmpElevationView* GbmpModelViewUtil::CreateNorthElevationView(IDocument* pDoc)
{
    GbmpElevationViewInput input;
    input.Document = pDoc;
    input.Name = GBMP_TR(L"北");
    input.ViewDirection = Vector3d::NegaUnitY;
    input.Origin = Vector3d(0, 12500.0f, 0);
    input.RightDirection = Vector3d::NegaUnitX;
    input.UpDirection = Vector3d::UnitZ;
    input.HasRectViewport = true;
    input.RectViewportWidth = 50000.0f;
    input.RectViewportHeight = 30000.0f;
    input.IsBuiltIn = true;

    return IGbmpElevationView::Create(input);
}

ElementId GbmpModelViewUtil::GetWorkLevelId(const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_UNLESS(pModelView, ElementId::InvalidID, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    const IGbmpModelView* pGbmpModelView = GetGbmpModelView(pModelView);
    DBG_WARN_AND_RETURN_UNLESS(pGbmpModelView, ElementId::InvalidID, L"pGbmpModelView为空",L"GDMPLab",L"2024-03-30");

    return pGbmpModelView->GetWorkLevelId();
}

bool GbmpModelViewUtil::SetWorkLevelId(IModelView* pModelView, ElementId workLevelId)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    IGbmpModelView* pGbmpModelView = GetGbmpModelView(pModelView);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pGbmpModelView, L"pGbmpModelView为空",L"GDMPLab",L"2024-03-30");

    return pGbmpModelView->SetWorkLevelId(workLevelId);
}

ElementId GbmpModelViewUtil::GetLowestLevelId(IDocument* pDoc)
{
    DBG_WARN_AND_RETURN_UNLESS(pDoc, ElementId::InvalidID, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    std::vector<const ILevel*> allLevels = LevelUtils::GetLevelsFromLowToHigh(pDoc);
    if (allLevels.empty())
    {
        return ElementId::InvalidID;
    }

    return allLevels.front()->GetOwnerElement()->GetElementId();
}

bool GbmpModelViewUtil::GetNextModelViewName(IDocument* pDoc, const std::wstring& viewType, const std::wstring& viewNameBasePart, const std::wstring& separator, std::wstring& nextViewName)
{
    int nMaxSuffix = 0;
    const auto& modelviews = IModelView::GetAllModelViews(pDoc);
    FOR_EACH(pModelView, modelviews)
    {
        if (pModelView && viewType == pModelView->GetUserDataId())
        {
            std::wstring modelViewName = pModelView->GetName();
            int nSuffix = -1;
            if (GetModelViewNameSuffix(modelViewName, viewNameBasePart, separator, nSuffix))
            {
                if (nSuffix > nMaxSuffix)
                {
                    nMaxSuffix = nSuffix;
                }
            }
        }
    }

    nextViewName = viewNameBasePart + separator + StringUtil::ToWString(nMaxSuffix + 1);
    return true;
}

bool GbmpModelViewUtil::GetModelViewNameSuffix(const std::wstring& viewName, const std::wstring& viewNameBasePart, const std::wstring& separator, int& nSuffix)
{
    const std::wstring strHeader = viewNameBasePart + separator;
    if (viewName.find(strHeader, 0) != 0)
        return false;

    const std::wstring strSuffix = viewName.substr(strHeader.length(), viewName.length() - strHeader.length());
    if (strSuffix.empty())
        return false;

    bool bOk = false;
    nSuffix = StringUtil::ToNumber<int>(strSuffix, &bOk);
    return bOk;
}

ElementId GbmpModelViewUtil::Get3DViewNextWorkLevel(const IDocument* pDoc, ElementId workLevelToBeDeleted)
{
    const auto& levels = LevelUtils::GetLevelsFromLowToHigh(pDoc);
    if (levels.size() > 0)
    {
        ElementId levelId = levels[0]->GetOwnerElement()->GetElementId();
        if (levelId != workLevelToBeDeleted)
        {
            return levelId;
        }
        else
        {
            if (levels.size() > 1)
            {
                return levels[1]->GetOwnerElement()->GetElementId();
            }
        }
    }

    return ElementId::InvalidID;
}

ElementId GbmpModelViewUtil::GetSectionViewNextWorkLevel(const IDocument* pDoc, ElementId workLevelToBeDeleted)
{
    return Get3DViewNextWorkLevel(pDoc, workLevelToBeDeleted);
}



IGbmpModelView* GbmpModelViewUtil::GetGbmpModelView(IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    if (IExternalDataComponent * pExternalDataComponent = pModelView->GetExternalDataComponent())
    {
        IExternalData* pData = pExternalDataComponent->FindExternalData(MODEL_VIEW_EXTERNAL_DATA_ID);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pData, L"pData为空",L"GDMPLab",L"2024-03-30");
        return dynamic_cast<IGbmpModelView*>(pData);
    }
    return nullptr;
}

const IGbmpModelView* GbmpModelViewUtil::GetGbmpModelView(const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    if (const IExternalDataComponent * pExternalDataComponent = pModelView->GetExternalDataComponent())
    {
        const IExternalData* pData = pExternalDataComponent->FindExternalData(MODEL_VIEW_EXTERNAL_DATA_ID);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pData, L"pData为空",L"GDMPLab",L"2024-03-30");
        return dynamic_cast<const IGbmpModelView*>(pData);
    }
    return nullptr;
}

bool GbmpModelViewUtil::GetAllVisibleGraphicsShapesBoundingBox(const IModelView* pModelView, Box3d& box)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    IDocument* pDoc = pModelView->GetDocument();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    Box3d sceneBox;
    const GraphicsElementShapes& visibleGReps = pModelView->GetVisibleModelViewElementShapesWithoutCroppedByViewPort();
    for (auto visibleGRep : visibleGReps)
    {
        const IGraphicsElementShape* pGRep = visibleGRep.second.Get();
        DBG_WARN_AND_CONTINUE_UNLESS(pGRep, L"pGRep为空",L"GDMPLab",L"2024-03-30");

        Box3d grepBox = GraphicsNodeUtils::CalculateGraphicsNodeBoundingBox(pDoc, pGRep);
        sceneBox.MergeBox(grepBox);
    }

    box = sceneBox;
    return true;
}

bool GbmpModelViewUtil::GetSectionViewBox(const IModelView* pModelView, gcmp::TiltBox3d& modelViewBox)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    IViewClipRange* pViewClipRange = IViewClipRange::GetViewClipRange(pModelView->GetDocument(), pModelView->GetViewClipRangeId());
    // 视图参数
    const std::wstring& viewType = pModelView->GetUserDataId();
    Vector3d originView = pModelView->GetOrigin();
    const Vector3d dirView = pModelView->GetViewDirection();
    const Vector3d upView = pModelView->GetUpDirection();
    const Vector3d rightView = dirView.Cross(upView);

    double leftOffset(0.0), rightOffset(0.0), upOffset(0.0), downOffset(0.0);
    bool bOk = pViewClipRange->GetRectViewClipRange(leftOffset, rightOffset, upOffset, downOffset);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(bOk, L"GetDefaultRectViewport失败",L"GDMPLab",L"2024-03-30");

    double topOffset(0.0);
    bOk = pModelView->GetTopPlaneOffset(topOffset);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(bOk, L"GetTopPlaneOffset失败",L"GDMPLab",L"2024-03-30");

    double bottomOffset(0.0);
    bOk = pModelView->GetBottomPlaneOffset(bottomOffset);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(bOk, L"GetBottomPlaneOffset失败",L"GDMPLab",L"2024-03-30");

    // 构造包围盒
    Vector3d ptLocalBoxMin;
    Vector3d ptLocalBoxMax;
    ptLocalBoxMin.SetZ(topOffset >= bottomOffset ? bottomOffset : topOffset);
    ptLocalBoxMax.SetZ(topOffset >= bottomOffset ? topOffset : bottomOffset);
    ptLocalBoxMin.SetX(-leftOffset);
    ptLocalBoxMax.SetX(rightOffset);
    ptLocalBoxMin.SetY(-downOffset);
    ptLocalBoxMax.SetY(upOffset);
    Box3d boxLocal(ptLocalBoxMin, ptLocalBoxMax);

    Coordinate3d viewBoxCoord(originView, rightView, upView, viewType == PlanViewType ? -dirView : dirView);

    modelViewBox = TiltBox3d(viewBoxCoord, boxLocal);

    return true;
}

std::wstring GbmpModelViewUtil::GetWorkPlaneName(const IModelView* pModelView)
{
    std::wstring resName;
    DBG_WARN_AND_RETURN_UNLESS(pModelView, resName, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    IWorkPlane* pWorkPlane = quick_cast<IWorkPlane>(pModelView->GetDocument()->GetElement(pModelView->GetWorkPlaneId()));
    if (pWorkPlane != nullptr)
    {
        resName = pWorkPlane->GetBasicInformation()->GetName();
    }
    return resName;
}

bool GbmpModelViewUtil::SetWorkPlaneName(const IModelView* pModelView, const std::wstring& name)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    IWorkPlane* pWorkPlane = quick_cast<IWorkPlane>(pModelView->GetDocument()->GetElement(pModelView->GetWorkPlaneId()));
    if (pWorkPlane != nullptr)
    {
        pWorkPlane->GetBasicInformation()->SetName(name);
        return true;
    }
    return false;
}

bool GbmpModelViewUtil::SetReferencePlaneIdOfWorkPlane(IModelView* pModelView, const ElementId& refID)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    IWorkPlane* pWorkPlane = quick_cast<IWorkPlane>(pModelView->GetDocument()->GetElement(pModelView->GetWorkPlaneId()));
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pWorkPlane, L"pWorkPlane为空",L"GDMPLab",L"2024-03-30");

    pWorkPlane->SetReferencedPlaneId(refID);

    return true;
}

Coordinate3d GbmpModelViewUtil::GetWorkPlaneCoordinate(const IModelView* pModelView)
{
    Coordinate3d coord;
    DBG_WARN_AND_RETURN_UNLESS(pModelView, coord, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IPlane> pPlane = pModelView->GetWorkPlane();
    DBG_WARN_AND_RETURN_UNLESS(pPlane, coord, L"pPlane为空",L"GDMPLab",L"2024-03-30");

    return pPlane->GetCoordinate();
}

IModelView* GbmpModelViewUtil::FindViewByName(IDocument* pDoc, const std::wstring& viewType, const std::wstring& viewName)
{
    const std::vector<IModelView*>& modelViews = IModelView::GetAllModelViews(pDoc);
    FOR_EACH_INDEX(i, modelViews)
    {
        IModelView* pModelView = modelViews[i];
        DBG_WARN_AND_CONTINUE_UNLESS(pModelView != nullptr, L"pModelView无效",L"GDMPLab",L"2024-03-30");
        std::wstring strName = pModelView->GetBasicInformation()->GetName();
        if (viewType == pModelView->GetUserDataId() && strName == viewName)
        {
            return pModelView;
        }
    }
    return nullptr;
}

gcmp::ElementId gcmp::GbmpModelViewUtil::GetReferencePlaneIdOfWorkPlane(const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_UNLESS(pModelView, ElementId::InvalidID, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    const IWorkPlane* pWorkPlane = quick_cast<IWorkPlane>(pModelView->GetDocument()->GetElement(pModelView->GetWorkPlaneId()));
    if (pWorkPlane != nullptr)
    {
        IReferencePlane* pRefPlane = quick_cast<IReferencePlane>(pModelView->GetDocument()->GetElement(pWorkPlane->GetReferencedPlaneId()));
        if (pRefPlane != nullptr)
        {
            return pWorkPlane->GetReferencedPlaneId();
        }
    }

    return ElementId::InvalidID;
}

gcmp::IModelView* gcmp::GbmpModelViewUtil::FindViewByBuiltinIndex(IDocument* pDoc, const std::wstring& viewType)
{
    const std::vector<IModelView*>& modelViews = IModelView::GetAllModelViews(pDoc);
    FOR_EACH_INDEX(i, modelViews)
    {
        IModelView* pModelView = modelViews[i];
        DBG_WARN_AND_CONTINUE_UNLESS(pModelView != nullptr, L"pModelView无效",L"GDMPLab",L"2024-03-30");
        if (viewType == pModelView->GetUserDataId())
        {
            return pModelView;
        }
    }
    return nullptr;
}

bool GbmpModelViewUtil::IsModelViewPlaneParallelToSectionBoxUpDownPlane(const Vector3d& pModelViewDir, const Vector3d& pSectionViewDir, const Vector3d& pSectionViewUpDir)
{
    return Vector3dUtils::IsPerpendicular(pModelViewDir, pSectionViewDir) && Vector3dUtils::IsParallel(pSectionViewUpDir, pModelViewDir);
}

bool GbmpModelViewUtil::IsModelViewPlaneParallelToSectionBoxLeftRightPlane(const Vector3d& pModelViewDir, const Vector3d& pSectionViewDir, const Vector3d& pSectionViewUpDir)
{
    return Vector3dUtils::IsPerpendicular(pModelViewDir, pSectionViewDir) && Vector3dUtils::IsPerpendicular(pSectionViewUpDir, pModelViewDir);
}
